package com.hzj.rtsp.factories;

import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.AsyncContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;

/**
 * javacv转包装<br/>
 * 无须转码，更低的资源消耗，更低的延迟<br/>
 * 确保流来源视频H264格式,音频AAC格式
 * 
 * @author hzj
 */
public class HLSConverterFactories extends Thread implements Converter {

	private static Logger logger	= LoggerFactory.getLogger( HLSConverterFactories.class );

	public volatile boolean runing = true;
	/**
	 * 读流器
	 */
	private FFmpegFrameGrabber grabber;
	/**
	 * 转码器
	 */
	private FFmpegFrameRecorder recorder;

	/**
	 * 转FLV格式的头信息<br/>
	 * 如果有第二个客户端播放首先要返回头信息
	 */
	private byte[] headers;
	/**
	 * 保存转换好的流
	 */
	private ByteArrayOutputStream stream;
	/**
	 * 流地址，h264,aac
	 */
	private String url;
	/**
	 * 流输出
	 */
	private List<AsyncContext> outEntitys;

	/**
	 * key用于表示这个转换器
	 */
	private String key;

	/**
	 * 转换队列
	 */
	private Map<String, Converter> factories;

	public HLSConverterFactories(String url, String key, Map<String, Converter> factories, List<AsyncContext> outEntitys) {
		this.url = url;
		this.key = key;
		this.factories = factories;
		this.outEntitys = outEntitys;
	}

	@Override
	public void run() {
		boolean isCloseGrabberAndResponse = true;
		try {
			//抓取
			grabber = new FFmpegFrameGrabber(url);
			if ("rtsp".equals(url.substring(0, 4))) {
				grabber.setOption("rtsp_transport", "tcp");
				grabber.setOption("stimeout", "5000000");
			}
			grabber.start();

			int bitrate = 2500000;// 比特率
			double framerate;// 帧率
			int err_index = 0;// 推流过程中出现错误的次数
			int timebase;// 时钟基
			long dts = 0, pts = 0;// pkt的dts、pts时间戳
			//end
			// 开启ffmpeg日志级别；QUIET是屏蔽所有，可选INFO、DEBUG、ERROR等


			// 异常的framerate，强制使用25帧
			if (grabber.getFrameRate() > 0 && grabber.getFrameRate() < 100) {
				framerate = grabber.getFrameRate();
			} else {
				framerate = 25.0;
			}

			bitrate = grabber.getVideoBitrate();// 获取到的比特率 0

				stream = new ByteArrayOutputStream();
				recorder = new FFmpegFrameRecorder(url, grabber.getImageWidth(), grabber.getImageHeight(),
						grabber.getAudioChannels());

				System.out.println("11111");
				// 设置比特率
				recorder.setVideoBitrate(bitrate);
				// h264编/解码器
				recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
				// 设置音频编码
				recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
				// 视频帧率(保证视频质量的情况下最低25，低于25会出现闪屏)
				recorder.setFrameRate(framerate);
				// 关键帧间隔，一般与帧率相同或者是视频帧率的两倍
				recorder.setGopSize((int) framerate);
				// 解码器格式
				recorder.setFormat("hls");
				// 单个切片时长,单位是s，默认为2s
				recorder.setOption("hls_time", "60");
				// HLS播放的列表长度，0标识不做限制
				recorder.setOption("hls_list_size", "0");
				// 设置切片的ts文件序号起始值，默认从0开始，可以通过此项更改
				recorder.setOption("start_number", "120");
				/////
			AVFormatContext fc = null;
			fc = grabber.getFormatContext();
			recorder.start(fc);
			// 清空探测时留下的缓存
//			grabber.flush();

			AVPacket pkt = null;

			for (int no_pkt_index = 0; no_pkt_index < 5 && err_index < 5;) {

				pkt = grabber.grabPacket();
				if (pkt == null || pkt.size() <= 0 || pkt.data() == null) {
					Thread.sleep(1);
					no_pkt_index++;
					continue;
				}
				System.out.println("----------------------");
				// 获取到的pkt的dts，pts异常，将此包丢弃掉。
				if (pkt.dts() == avutil.AV_NOPTS_VALUE && pkt.pts() == avutil.AV_NOPTS_VALUE || pkt.pts() < dts) {
					err_index++;
					av_packet_unref(pkt);
					continue;
				}
				// 矫正dts，pts
				pkt.pts(pts);
				pkt.dts(dts);
				err_index += (recorder.recordPacket(pkt) ? 0 : 1);
				// pts,dts累加
				timebase = grabber.getFormatContext().streams(pkt.stream_index()).time_base().den();

				pts += (timebase / (int) framerate);
				dts += (timebase / (int) framerate);
			}

		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.info("播放失败");
			//可以在这里写个错误图片流返回，这样播放器就能看到问题了
		} finally {
			closeConverter(isCloseGrabberAndResponse);
			completeResponse(isCloseGrabberAndResponse);
			logger.info("this url:{} converterFactories exit", url);

		}
	}

	/**
	 * 输出FLV视频流
	 * 
	 * @param b
	 */
	public void writeResponse(byte[] b) {
		Iterator<AsyncContext> it = outEntitys.iterator();
		while (it.hasNext()) {
			AsyncContext o = it.next();
			try {
				o.getResponse().getOutputStream().write(b);
			} catch (Exception e) {
				logger.info("移除一个输出");
				it.remove();
			}
		}
	}

	/**
	 * 退出转换
	 */
	public void closeConverter(boolean isCloseGrabberAndResponse) {
		try{
			grabber.close();
			recorder.close();
			stream.close();
		}catch (Exception e){}

	}

	/**
	 * 关闭异步响应
	 * 
	 * @param isCloseGrabberAndResponse
	 */
	public void completeResponse(boolean isCloseGrabberAndResponse) {
		if (isCloseGrabberAndResponse) {
			Iterator<AsyncContext> it = outEntitys.iterator();
			while (it.hasNext()) {
				AsyncContext o = it.next();
				o.complete();
			}
		}
	}

	@Override
	public String getKey() {
		return this.key;
	}

	@Override
	public String getUrl() {
		return this.url;
	}

	@Override
	public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException {
		if (headers == null) {
			outEntitys.add(entity);
		} else {
			entity.getResponse().getOutputStream().write(headers);
			entity.getResponse().getOutputStream().flush();
			outEntitys.add(entity);
		}
	}

	@Override
	public void exit() {
		this.runing = false;
		try {
			this.join();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}



}
